#include "waveformthread.h"

typedef QVector<double> DoubleVector;

WaveFormThread::WaveFormThread(QString fileName) :
    QThread()
{
    live = true;
    action = NOT;
    this->fileName = fileName;
    qRegisterMetaType<DoubleVector>("QVector<double>");
}

WaveFormThread::~WaveFormThread()
{
    delete wavebufL;
    delete wavebufR;
    BASS_StreamFree(decoder); // free the decoder*/
    live = false;
}

void WaveFormThread::run()
{
        create(fileName);
        while(live) {
            switch (action) {
            case NOT:
                break;
            case ADD:
                addFX();
                action = NOT;
                break;
            case DEL:
                delFX();
                action = NOT;
                break;
            }
            this->sleep(1);
        }
}

void WaveFormThread::create(QString fileName)
{
    decoder = BASS_StreamCreateFile(FALSE,fileName.toLocal8Bit().data(),0,0,BASS_SAMPLE_FLOAT | BASS_STREAM_DECODE | BASS_SAMPLE_LOOP);
    scanAudio();
    addLeftChannelGraph();
    addRightChannelGraph();
}

void WaveFormThread::scanAudio()
{
    int counter = 0;
    DWORD cpos=0;
    DWORD peak[2]={0};
    DWORD level;
    DWORD pos;

    BYTE_LENGTH = BASS_ChannelGetLength(decoder, BASS_POS_BYTE);//полная длина в байтах
    TIME_LENGTH = BASS_ChannelBytes2Seconds(decoder, BYTE_LENGTH);//корректная длина в секундах

    bpp = BASS_ChannelSeconds2Bytes(decoder,0.02); // частота пиков

    WIDTH = BYTE_LENGTH / bpp + 1;

    if (wavebufL != NULL) {
        delete wavebufL;
        delete wavebufR;
    }
    wavebufL = new double[WIDTH];
    wavebufR = new double[WIDTH];

    for (int i =0; i < WIDTH; i++) {
        level = BASS_ChannelGetLevel(decoder); // scan peaks
        if (peak[0]<LOWORD(level)) peak[0]=LOWORD(level); // set left peak
        if (peak[1]<HIWORD(level)) peak[1]=HIWORD(level); // set right peak

        if (BASS_ChannelIsActive(decoder) != BASS_ACTIVE_PLAYING) {
            break; // reached the end
        }
        else pos = BASS_ChannelGetPosition(decoder, BASS_POS_BYTE)/bpp;

        if (pos > cpos) {
            counter++;
            if (counter < WIDTH - 1) {
                wavebufL[i] = (double)peak[0] / 32768;
                wavebufR[i] = (double)peak[1] / 32768;
            }

            if (pos >= WIDTH) break;
            cpos = pos;
        }
        peak[0] = 0;
        peak[1] = 0;
    }
}

void WaveFormThread::rescan()
{
    scanAudio();
    addLeftChannelGraph();
    addRightChannelGraph();
}

void WaveFormThread::addFX()
{
    HFX fx = BASS_ChannelSetFX(decoder, type, 0);
    BASS_FXSetParameters(fx, params);
    effects.push_back(fx);
    rescan();
}

void WaveFormThread::addFX(DWORD type, const void* params)
{
    this->type = type;
    this->params = params;
    action = ADD;
}

void WaveFormThread::delEffects()
{
    action = DEL;
}

void WaveFormThread::delFX()
{
    for (int i = 0; i < effects.size(); i++)
        BASS_ChannelRemoveFX(decoder, effects[i]);
    effects.clear();
    rescan();
}

//ДОБАВЛЕНИЕ ГРАФИКА ЛЕВОГО КАНАЛА
void WaveFormThread::addLeftChannelGraph()
{
  int time = TIME_LENGTH;
  int step = WIDTH / time;
  int eps = 25;
  step /= eps;
  int width = time * eps;
  DoubleVector x(width), y(width);
  for (int i=0; i < width - 1; i++)
  {
    x[i] = (double)i / eps;
    y[i] = wavebufL[i * step];
  }
  x[width - 1] = time;
  y[width - 1] = wavebufL[WIDTH - 1];
  emit clear();
  emit leftData(x, y);
}

//ДОБАВЛЕНИЕ ГРАФИКА ПРАВОГО КАНАЛА
void WaveFormThread::addRightChannelGraph()
{
    int time = TIME_LENGTH;
    int step = WIDTH / time;
    int eps = 25;
    step /= eps;
    int width = time * eps;
    DoubleVector x(width), y(width);
  for (int i=0; i < width - 1; i++) {
      x[i] = (double)i / eps;
      y[i] = wavebufR[i * step];
      y[i] = -y[i];
  }
  x[width - 1] = time;
  y[width - 1] = wavebufR[WIDTH - 1];
  y[width - 1] = -y[width - 1];
  emit rightData(x, y);
  emit addSlider();
}
